home *** CD-ROM | disk | FTP | other *** search
/ ShareWare OnLine 2 / ShareWare OnLine Volume 2 (CMS Software)(1993).iso / os2 / remin301.zip / REMIN300.ZIP / TRIGGER.C < prev    next >
C/C++ Source or Header  |  1992-11-10  |  14KB  |  460 lines

  1. /***************************************************************/
  2. /*                                                             */
  3. /*  TRIGGER.C                                                  */
  4. /*                                                             */
  5. /*  Routines for figuring out the trigger date of a reminder   */
  6. /*                                                             */
  7. /*  This file is part of REMIND.                               */
  8. /*  Copyright (C) 1991 by David F. Skoll.                      */
  9. /*                                                             */
  10. /***************************************************************/
  11.  
  12. #include <stdio.h>
  13. #include "config.h"
  14. #ifdef HAVE_STDLIB_H
  15. #include <stdlib.h>
  16. #endif
  17. #ifdef HAVE_MALLOC_H
  18. #include <malloc.h>
  19. #endif
  20. #include "types.h"
  21. #include "expr.h"
  22. #include "protos.h"
  23. #include "globals.h"
  24. #include "err.h"
  25.  
  26. #define GOT_DAY 1
  27. #define GOT_MON 2
  28. #define GOT_YR 4
  29. #define GOT_WD 8
  30.  
  31. static int    JYear        ARGS((int jul));
  32. static int    JMonth        ARGS((int jul));
  33. static int NextSimpleTrig ARGS((int startdate, Trigger *trig, int *err));
  34. static int GetNextTriggerDate ARGS((Trigger *trig, int start, int *err, int *nextstart));
  35.  
  36. /***************************************************************/
  37. /*                                                             */
  38. /*  NextSimpleTrig                                             */
  39. /*                                                             */
  40. /*  Compute the "simple" trigger date, taking into account     */
  41. /*  ONLY the day of week, day, month and year components.      */
  42. /*  Normally, returns -1 if the trigger has expired.  As a     */
  43. /*  special case, if D, M, Y [WD] are specified, returns the   */
  44. /*  Julian date, regardless of whether it's expired.  This is  */
  45. /*  so that dates with a REP can be handled properly.          */
  46. /*                                                             */
  47. /***************************************************************/
  48. #ifdef HAVE_PROTOS
  49. PRIVATE int NextSimpleTrig(int startdate, Trigger *trig, int *err)
  50. #else
  51. static int NextSimpleTrig(startdate, trig, err)
  52. int startdate;
  53. Trigger *trig;
  54. int *err;
  55. #endif
  56. {
  57.    int typ = 0;
  58.    int d, m, y, j, d2, m2, y2;
  59.  
  60.    *err = 0;
  61.    FromJulian(startdate, &y, &m, &d);
  62.    d2 = d;
  63.    m2 = m;
  64.    y2 = y;
  65.  
  66.    if (trig->d != NO_DAY) typ |= GOT_DAY;
  67.    if (trig->m != NO_MON) typ |= GOT_MON;
  68.    if (trig->y != NO_YR) typ |= GOT_YR;
  69.    if (trig->wd != NO_WD) typ |= GOT_WD;
  70.    switch(typ) {
  71.       case 0:
  72.       case GOT_WD:
  73.      if (trig->wd != NO_WD)
  74.         while(! (trig->wd & (1 << (startdate%7)))) startdate++;
  75.      return startdate;
  76.  
  77.       case GOT_DAY:
  78.      if (d > trig->d) {
  79.         m++;
  80.         if (m == 12) { m = 0; y++; }
  81.      }
  82.      while (trig->d > DaysInMonth(m, trig->y)) m++;
  83.      j = Julian(y, m, trig->d);
  84.      return j;
  85.  
  86.       case GOT_MON:
  87.      if (m == trig->m) return startdate;
  88.      else if (m > trig->m) return Julian(y+1, trig->m, 1);
  89.      else return Julian(y, trig->m, 1);
  90.  
  91.       case GOT_YR:
  92.      if (y == trig->y) return startdate;
  93.      else if (y < trig->y) return Julian(trig->y, 0, 1);
  94.      else return -1;
  95.  
  96.       case GOT_DAY+GOT_MON:
  97.      if (m > trig->m || m == trig->m && d > trig->d) y++;
  98.      if (trig->d > MonthDays[trig->m]) {
  99.         *err = E_BAD_DATE;
  100.         return -1;
  101.      }
  102.  
  103.      /* Take care of Feb. 29 */
  104.      while (trig->d > DaysInMonth(trig->m, y)) y++;
  105.      return Julian(y, trig->m, trig->d);
  106.  
  107.       case GOT_DAY+GOT_YR:
  108.     if (y < trig->y) return Julian(trig->y, 0, trig->d);
  109.     else if (y > trig->y) return -1;
  110.  
  111.     if (d > trig->d) {
  112.        m++;
  113.        if (m == 12) return -1;
  114.     }
  115.     while (trig->d > DaysInMonth(m, trig->y)) m++;
  116.     return Julian(trig->y, m, trig->d);
  117.  
  118.       case GOT_MON+GOT_YR:
  119.      if (y > trig->y || (y == trig->y && m > trig->m)) return -1;
  120.      if (y < trig->y) return Julian(trig->y, trig->m, 1);
  121.      if (m == trig->m) return startdate;
  122.      return Julian(trig->y, trig->m, 1);
  123.  
  124.       case GOT_DAY+GOT_MON+GOT_YR:
  125.      if (trig->d > DaysInMonth(trig->m, trig->y)) {
  126.         *err = E_BAD_DATE;
  127.         return -1;
  128.      }
  129.      return Julian(trig->y, trig->m, trig->d);
  130.  
  131.       case GOT_YR+GOT_WD:
  132.      if (y > trig->y) return -1;
  133.      if (y < trig->y) j = Julian(trig->y, 0, 1);
  134.      else j = startdate;
  135.      while(! (trig->wd & (1 << (j%7)))) j++;
  136.      if (JYear(j) > trig->y) return -1;
  137.      return j;
  138.  
  139.       case GOT_MON+GOT_WD:
  140.      if (m == trig->m) {
  141.         j = startdate;
  142.         while(! (trig->wd & (1 << (j%7)))) j++;
  143.         if (JMonth(j) == trig->m) return j;
  144.      }
  145.      if (m >= trig->m) j = Julian(y+1, trig->m, 1);
  146.      else if (m < trig->m) j = Julian(y, trig->m, 1);
  147.      while(! (trig->wd & (1 << (j%7)))) j++;
  148.      return j; /* Guaranteed to be within the month */
  149.  
  150.       case GOT_DAY+GOT_WD:
  151.      if (m !=0 || y > BASE) {
  152.         m2 = m-1;
  153.         if (m2 < 0) { y2 = y-1; m2 = 11; }
  154.  
  155.         /* If there are fewer days in previous month, no match */
  156.         if (trig->d <= DaysInMonth(m2, y2)) {
  157.            j = Julian(y2, m2, trig->d);
  158.            while(! (trig->wd & (1 << (j%7)))) j++;
  159.            if (j >= startdate) return j;
  160.  
  161.         }
  162.      }
  163.  
  164.      /* Try this month */
  165.      if (trig->d <= DaysInMonth(m, y)) {
  166.         j = Julian(y, m, trig->d);
  167.         while(! (trig->wd & (1 << (j%7)))) j++;
  168.         if (j >= startdate) return j;
  169.      }
  170.  
  171.          /* Argh!  Try next avail. month */
  172.      m2 = m+1;
  173.      if (m2 > 11) { m2 = 0; y++; }
  174.      while (trig->d > DaysInMonth(m2, y)) m2++;
  175.      j = Julian(y, m2, trig->d);
  176.      while(! (trig->wd & (1 << (j%7)))) j++;
  177.      return j;
  178.  
  179.       case GOT_WD+GOT_YR+GOT_DAY:
  180.      if (y > trig->y+1 || y > trig->y && m>0) return -1;
  181.      if (y > trig->y) {
  182.         j = Julian(trig->y, 11, trig->d);
  183.         while(! (trig->wd & (1 << (j%7)))) j++;
  184.         if (j >= startdate) return j;
  185.      } else if (y < trig->y) {
  186.         j = Julian(trig->y, 0, trig->d);
  187.         while(! (trig->wd & (1 << (j%7)))) j++;
  188.         return j;
  189.      } else {
  190.         /* Try last month */
  191.         if (m > 0) {
  192.            m2 = m-1;
  193.            while (trig->d > DaysInMonth(m2, trig->y)) m2--;
  194.            j = Julian(trig->y, m2, trig->d);
  195.            while(! (trig->wd & (1 << (j%7)))) j++;
  196.            if (j >= startdate) return j;
  197.         }
  198.      }
  199.      /* Try this month */
  200.      if (trig->d <= DaysInMonth(m, trig->y)) {
  201.         j = Julian(trig->y, m, trig->d);
  202.         while(! (trig->wd & (1 << (j%7)))) j++;
  203.         if (j >= startdate) return j;
  204.      }
  205.  
  206.      /* Must be next month */
  207.      if (m == 11) return -1;
  208.      m++;
  209.      while (trig->d > DaysInMonth(m, trig->d)) m++;
  210.      j = Julian(trig->y, m, trig->d);
  211.      while(! (trig->wd & (1 << (j%7)))) j++;
  212.      return j;
  213.  
  214.       case GOT_DAY+GOT_MON+GOT_WD:
  215.      /* Move up to the first valid year */
  216.     while (trig->d > DaysInMonth(trig->m, y)) y++;
  217.  
  218.     /* Try this year */
  219.     j = Julian(y, trig->m, trig->d);
  220.     while(! (trig->wd & (1 << (j%7)))) j++;
  221.     if (j >= startdate) return j;
  222.  
  223.     /* Must be next year */
  224.     y = y + 1;
  225.     while (trig->d > DaysInMonth(trig->m, y)) y++;
  226.     j = Julian(y, trig->m, trig->d);
  227.     while(! (trig->wd & (1 << (j%7)))) j++;
  228.     return j;
  229.  
  230.       case GOT_WD+GOT_MON+GOT_YR:
  231.      if (y > trig->y || (y == trig->y && m > trig->m)) return -1;
  232.      if (trig->y > y || (trig->y == y && trig->m > m)) {
  233.         j = Julian(trig->y, trig->m, 1);
  234.         while(! (trig->wd & (1 << (j%7)))) j++;
  235.         return j;
  236.      } else {
  237.         j = startdate;
  238.         while(! (trig->wd & (1 << (j%7)))) j++;
  239.         FromJulian(j, &y2, &m2, &d2);
  240.         if (m2 == trig->m) return j; else return -1;
  241.      }
  242.  
  243.       case GOT_WD+GOT_DAY+GOT_MON+GOT_YR:
  244.      j = Julian(trig->y, trig->m, trig->d);
  245.      while(! (trig->wd & (1 << (j%7)))) j++;
  246.      return j;
  247.  
  248.       default:
  249.     Eprint("NextSimpleTrig: Bad type %d", typ);
  250.     *err = E_SWERR;
  251.     return -1;
  252.    }
  253. }
  254.  
  255. /***************************************************************/
  256. /*                                                             */
  257. /*  JMonth - Given a Julian date, what's the month?            */
  258. /*                                                             */
  259. /***************************************************************/
  260. #ifdef HAVE_PROTOS
  261. PRIVATE int JMonth(int jul)
  262. #else
  263. static int JMonth(jul)
  264. int jul;
  265. #endif
  266. {
  267.    int y, m, d;
  268.    FromJulian(jul, &y, &m, &d);
  269.    return m;
  270. }
  271.  
  272. /***************************************************************/
  273. /*                                                             */
  274. /*  JYear - Given a Julian date, what's the year?              */
  275. /*                                                             */
  276. /***************************************************************/
  277. #ifdef HAVE_PROTOS
  278. PRIVATE int JYear(int jul)
  279. #else
  280. static int JYear(jul)
  281. int jul;
  282. #endif
  283. {
  284.    int y, m, d;
  285.    FromJulian(jul, &y, &m, &d);
  286.    return y;
  287. }
  288.  
  289. /***************************************************************/
  290. /*                                                             */
  291. /*  GetNextTriggerDate                                         */
  292. /*                                                             */
  293. /*  Given a trigger, compute the next trigger date.            */
  294. /*                                                             */
  295. /*  Returns the Julian date of next trigger, -1 if             */
  296. /*  expired, -2 if can't compute trigger date.                 */
  297. /*                                                             */
  298. /***************************************************************/
  299. #ifdef HAVE_PROTOS
  300. PRIVATE int GetNextTriggerDate(Trigger *trig, int start, int *err, int *nextstart)
  301. #else
  302. static int GetNextTriggerDate(trig, start, err, nextstart)
  303. Trigger *trig;
  304. int start;
  305. int *err;
  306. int *nextstart;
  307. #endif
  308. {
  309.    int simple, mod;
  310.  
  311. /* First:  Have we passed the UNTIL date? */
  312.    if (trig->until != NO_UNTIL &&
  313.        trig->until < start) return -1; /* expired */
  314.  
  315. /* Next: If it's an "AFTER"-type skip, back up
  316.    until we're at the start of a block of holidays */
  317.    if (trig->skip == AFTER_SKIP)
  318.       while (IsOmitted(start-1, trig->localomit)) start--;
  319.  
  320. /* Find the next simple trigger */
  321.    simple = NextSimpleTrig(start, trig, err);
  322.  
  323. /* Problems? */
  324.    if (*err || (simple == -1)) return -1;
  325.  
  326. /* Suggested starting point for next attempt */
  327.    *nextstart = simple+1;
  328.  
  329. /* If there's a BACK, back up... */
  330.    if (trig->back != NO_BACK) {
  331.       mod = trig->back;
  332.       if (mod < 0) simple += mod;
  333.       else
  334.      while(mod) {
  335.         simple--;
  336.         if (!IsOmitted(simple, trig->localomit)) mod--;
  337.      }
  338.    }
  339.  
  340. /* If there's a REP, calculate the next occurrence */
  341.    if (trig->rep != NO_REP) {
  342.       if (simple < start) {
  343.      mod = (start - simple) / trig->rep;
  344.      simple = simple + mod * trig->rep;
  345.      if (simple < start) simple += trig->rep;
  346.       }
  347.    }
  348.  
  349. /* If it's a "BEFORE"-type skip, back up */
  350.    if (trig->skip == BEFORE_SKIP)
  351.       while(IsOmitted(simple, trig->localomit)) simple--;
  352.  
  353. /* If it's an "AFTER"-type skip, jump ahead */
  354.    if (trig->skip == AFTER_SKIP)
  355.       while (IsOmitted(simple, trig->localomit)) simple++;
  356.  
  357. /* Return the date */
  358.    return simple;
  359. }
  360.  
  361. /***************************************************************/
  362. /*                                                             */
  363. /*  ComputeTrigger                                             */
  364. /*                                                             */
  365. /*  The main function.  Compute the next trigger date given    */
  366. /*  today's date.                                              */
  367. /*                                                             */
  368. /***************************************************************/
  369. #ifdef HAVE_PROTOS
  370. PUBLIC int ComputeTrigger(int today, Trigger *trig, int *err)
  371. #else
  372. int ComputeTrigger(today, trig, err)
  373. int today;
  374. Trigger *trig;
  375. int *err;
  376. #endif
  377. {
  378.    int nattempts = 0,
  379.        start = today,
  380.        nextstart,
  381.        y, m, d,
  382.        result;
  383.  
  384.    LastTrigValid = 0;
  385. /* Assume everything works */
  386.    *err = OK;
  387.  
  388. /* But check for obvious problems... */
  389.    if (trig->localomit == 1 + 2 + 4 + 8 + 16 + 32 + 64) {
  390.       *err = E_2MANY_LOCALOMIT;
  391.       return -1;
  392.    }
  393.  
  394.    if (trig->rep != NO_REP &&
  395.        (trig->d == NO_DAY ||
  396.     trig->m == NO_MON ||
  397.     trig->y == NO_YR)) {
  398.       Eprint("Must fully specify date to use repeat.");
  399.       *err = E_PARSE_ERR;
  400.       return -1;
  401.    }
  402.        
  403.    
  404.    while (nattempts++ < TRIG_ATTEMPTS) {
  405.       result = GetNextTriggerDate(trig, start, err, &nextstart);
  406.  
  407.       /* If there's an error, die immediately */
  408.       if (*err) return -1;
  409.       if (result == -1) {
  410.          if (DebugFlag & DB_PRTTRIG) {
  411.         fprintf(ErrFp, "%s(%d): Expired\n",
  412.            FileName, LineNo);
  413.      }
  414.          return -1;
  415.       }
  416.  
  417.       /* If result is >= today, great! */
  418.       if (result >= today &&
  419.          (trig->skip != SKIP_SKIP || !IsOmitted(result, trig->localomit))) {
  420.      LastTriggerDate = result;  /* Save in global var */
  421.      LastTrigValid = 1;
  422.      if (DebugFlag & DB_PRTTRIG) {
  423.         FromJulian(result, &y, &m, &d);
  424.         fprintf(ErrFp, "%s(%d): Trig = %s, %d %s, %d\n",
  425.                 FileName, LineNo,
  426.             DayName[result % 7],
  427.             d,
  428.             MonthName[m],
  429.             y);
  430.      }
  431.      return result;
  432.       }
  433.  
  434.       /* If it's a simple trigger, no point in rescanning */
  435.       if (trig->back == NO_BACK &&
  436.           trig->skip == NO_SKIP &&
  437.       trig->rep == NO_REP) {
  438.           if (DebugFlag & DB_PRTTRIG) {
  439.          fprintf(ErrFp, "%s(%d): Expired\n",
  440.                  FileName, LineNo);
  441.          }
  442.          return -1;
  443.       }
  444.       /* Keep scanning... unless there's no point in doing it.*/
  445.       if (nextstart <= start) {
  446.          if (DebugFlag & DB_PRTTRIG) {
  447.         fprintf(ErrFp, "%s(%d): Expired\n",
  448.                   FileName, LineNo);
  449.          }
  450.      return -1;
  451.       }
  452.       else start = nextstart;
  453.  
  454.    }
  455.  
  456.    /* We failed - too many attempts or trigger has expired*/
  457.    *err = E_CANT_TRIG;
  458.    return -1;
  459. }
  460.